const std = @import("std");
const curses = @cImport({
	@cInclude("ncurses.h");
});


pub fn Grid(comptime CharType: type) type {
	return struct {
		width: u8,
		height: u8,
		empty_char: CharType,
		empty_color_pair_id: u8,
		window: *curses.struct__win_st,
		allocator: std.mem.Allocator = std.heap.page_allocator,
		_grid: [][]Cell(CharType) = undefined,

		const Self = @This();


		pub fn init(self: *Self) !void {
			const cell = Cell(CharType){
				.char = self.empty_char,
				.color_pair_id = self.empty_color_pair_id,
			};
			const rows: [][]Cell(CharType) = try self.allocator.alloc(
					[]Cell(CharType), self.height);
			for (0..rows.len) |i| {
				rows[i] = try self.allocator.alloc(Cell(CharType), self.width);
				@memset(rows[i], cell);
			}

			self._grid = rows;
		}


		pub fn deinit(self: *Self) void {
			for (0..self._grid.len) |i| {
				self.allocator.free(self._grid[i]);
			}
			self.allocator.free(self._grid);
			self._grid = &.{};
		}


		pub fn get_grid(self: *Self) [][]Cell(CharType) {
			return self._grid;
		}


		pub fn clear(self: *Self) void {
			const cell = Cell(CharType){
				.char = self.empty_char,
				.color_pair_id = self.empty_color_pair_id,
			};

			for (0..self._grid.len) |i| {
				@memset(self._grid[i], cell);
			}
		}


		pub fn set_cell(self: *Self, x: u8, y: u8, char: CharType,
				color_pair_id: u8) void {
			if (x < 0 or x >= self.width or y < 0 or y >= self.height) {
				return;
			}

			self._grid[y][x].char = char;
			self._grid[y][x].color_pair_id = color_pair_id;
		}


		pub fn print_cell(self: *Self, cell: *const Cell(CharType)) void {
			_ = self;

			const char: curses.wchar_t = cell.char;
			const wstr: [2]curses.wchar_t = .{char, 0};
			var cc: curses.cchar_t = curses.cchar_t{};
			_ = curses.setcchar(&cc, &wstr, 0, cell.color_pair_id, null);
			_ = curses.add_wch(&cc);
		}


		pub fn print_str(self: *Self, str: [*]const u8) void {
			_ = curses.waddstr(self.window, str);
		}


		pub fn set_cursor_pos(self: *Self, x: c_int, y: c_int) void {
			_ = curses.wmove(self.window, x, y);
		}


		pub fn show(self: *Self) void {
			_ = curses.wrefresh(self.window);
		}


		pub fn get_window_size_x(self: *Self) c_int {
			return curses.getmaxx(self.window);
		}


		pub fn get_window_size_y(self: *Self) c_int {
			return curses.getmaxy(self.window);
		}
	};
}


pub fn Cell(comptime CharType: type) type {
	return struct {
		char: CharType,
		color_pair_id: u8,
	};
}
